package producersConsumersTests.RemoverThroughput;

import java.util.List;
import java.util.Map;

import prod_cons.TaskContainer;
import producersConsumersTests.InserterThroughput.SleepyCollector;
import producersConsumersTests.utils.CounterRemover;
import producersConsumersTests.utils.DataGetter;
import producersConsumersTests.utils.DummyTask;
import producersConsumersTests.utils.DummyTaskFactory;
import producersConsumersTests.utils.Inserter;
import producersConsumersTests.utils.Test;
import producersConsumersTests.utils.Tester;
import producersConsumersTests.utils.TesterVisitor;

/**
 * Test #1 - removers throughput
 * 
 * This test measures how many tasks can get extracted from the container in one
 * second. To create minimal distortion, the removers count the tasks they
 * insert locally. An external thread (SleepyCollector) sleeps for 1sec and then
 * loops over all of them, counting how many tasks they all managed to get in
 * the past second.
 * 
 * 
 */
public class RemoverThroughputTest implements Test {
	private static class RemoverThroughputTester extends
			Tester<Inserter<DummyTask>, CounterRemover> {
		public RemoverThroughputTester(Inserter<DummyTask>[] inserters,
				CounterRemover[] removers, int timeToRun,
				TaskContainer container) {
			super(inserters, removers, timeToRun, container);
		}

		private Thread collectorThread;
		private SleepyCollector<CounterRemover> collector;
		private double average;

		@Override
		protected void beforeRun() {
			collector = new SleepyCollector<CounterRemover>(
					new DataGetter<CounterRemover>() {
						@Override
						public long getDataFrom(CounterRemover runner) {
							long val = runner.getTaskCount();
							runner.zeroCounter();
							return val;
						}
					}, removers);
			collectorThread = new Thread(collector);
			collectorThread.start();
		}

		@Override
		protected void afterRun() {
			collector.stop();
			try {
				collectorThread.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("Test complete!");
			long sum = 0, max = 0, count = 0;
			for (List<Long> l : collector.getResults().values()) {
				for (Long i : l) {
					sum += i;
					count++;
					max = i > max ? i : max;
				}
			}
			average = (double)sum/count;
			System.out.println("Inserters inserted "+average +" tasks/second");
		}

		public Map<Integer, List<Long>> getCollectorResults() {
			return collector.getResults();
		}

		public double getAverage() {
			return average;
		}
	}

	private static class DummyInserter extends Inserter<DummyTask> {
		public DummyInserter(TaskContainer container, int peakLength,
				int timeBetweenPeaks) {
			super(container, peakLength, timeBetweenPeaks,
					new DummyTaskFactory());
		}

		public DummyInserter(TaskContainer container, double lambda) {
			super(container, lambda, new DummyTaskFactory());
		}
	}

	private RemoverThroughputTester tester;

	@Override
	public void test(int insertersNum, int removersNum, int peakLength,
			int timeBetweenPeaks, int timeToRun, TaskContainer container,
			int extraInt) throws InterruptedException, InstantiationException,
			IllegalAccessException, ClassNotFoundException {
		DummyInserter inserters[] = new DummyInserter[insertersNum];
		for (int i = 0; i < inserters.length; i++)
			inserters[i] = new DummyInserter(container, peakLength,
					timeBetweenPeaks);
		CounterRemover removers[] = new CounterRemover[removersNum];
		for (int i = 0; i < removers.length; i++)
			removers[i] = new CounterRemover(container);
		tester = new RemoverThroughputTester(inserters, removers, timeToRun,
				container);
		tester.runTest();
	}

	@Override
	public void test(int insertersNum, int removersNum, double lambda,
			int timeToRun, TaskContainer container, int extraInt)
			throws InterruptedException, InstantiationException,
			IllegalAccessException, ClassNotFoundException {
		DummyInserter inserters[] = new DummyInserter[insertersNum];
		for (int i = 0; i < inserters.length; i++)
			inserters[i] = new DummyInserter(container, lambda);
		CounterRemover removers[] = new CounterRemover[removersNum];
		for (int i = 0; i < removers.length; i++)
			removers[i] = new CounterRemover(container);
		tester = new RemoverThroughputTester(inserters, removers, timeToRun,
				container);
		tester.runTest();
	}

	public Map<Integer, List<Long>> getResults() {
		return tester.getCollectorResults();
	}
	
	@Override
	public void accept(TesterVisitor v) {
		v.visit(this);
	}

	public double getAverage() {
		return tester.getAverage();
	}
}
